
#include "itplay.h"
#include <stddef.h>

#ifdef _IT_DEBUG_CUSTOM_H
	#include _IT_DEBUG_CUSTOM_H
#else
	#ifdef _IT_DEBUG
		#include <stdio.h>

		#define _IT_DEBUG_PRINTF( m_arg )\
		do {\
			fprintf(stdout , m_arg );\
		} while (0)

			#define _IT_ERROR_PRINT( m_arg )\
			do {\
				fprintf(stdout , "** %s:%i ** IT_PLAY ERROR:  ", __FILE__, __LINE__);\
				fprintf(stdout , m_arg );\
				fprintf(stdout , "\n");\
			} while (0)

	#else
		/* Or Implement as you wish */
		#define _IT_DEBUG_PRINT( m_arg ) \
		do { } while(0)
		#define _IT_ERROR_PRINT( m_arg ) \
		do { } while(0)

	#endif

#endif

#define LOADER_DEBUG
#ifdef LOADER_DEBUG
#include <stdio.h>
#endif

/* IT SAMPLE DECOMPRESSION CODE */

typedef struct {

	it_bool it215;
	it_u32 *source_buffer; 	/* source buffer */
	it_u32 *source_position;  /* actual reading position */
	it_u8 source_remaining_bits; /* bits remaining in read dword */
	IT_FileIO *file;
	IT_MemoryManager *memory;
	IT_SampleID sample_id;
	IT_Mixer *mixer;

} _IT_DecompressorState;

static it_u32 _read_n_bits_from_IT_compressed_block(_IT_DecompressorState *p_state, it_u8 p_bits_to_read) {

    it_u32 aux_return_value;
    it_u32 val;
    it_u8 *buffer=(it_u8*)p_state->source_position;

    if ( p_bits_to_read <= p_state->source_remaining_bits ) {

	val=buffer[3];
	val<<=8;
	val|=buffer[2];
	val<<=8;
	val|=buffer[1];
	val<<=8;
	val|=buffer[0];

	aux_return_value = val & ((1 << p_bits_to_read) - 1);
	val >>= p_bits_to_read;
	p_state->source_remaining_bits -= p_bits_to_read;

	buffer[3]=val>>24;
	buffer[2]=(val>>16)&0xFF;
	buffer[1]=(val>>8)&0xFF;
	buffer[0]=(val)&0xFF;

    } else {
	it_u32 nbits;

	aux_return_value=buffer[3];
	aux_return_value<<=8;
	aux_return_value|=buffer[2];
	aux_return_value<<=8;
	aux_return_value|=buffer[1];
	aux_return_value<<=8;
	aux_return_value|=buffer[0];

	nbits = p_bits_to_read - p_state->source_remaining_bits;
/*	aux_return_value = *p_state->source_position */
	p_state->source_position++;
	buffer+=4;
	val=buffer[3];
	val<<=8;
	val|=buffer[2];
	val<<=8;
	val|=buffer[1];
	val<<=8;
	val|=buffer[0];
	aux_return_value |= ((val & ((1 << nbits) - 1)) << p_state->source_remaining_bits);
	val >>= nbits;
	p_state->source_remaining_bits = 32 - nbits;
	buffer[3]=val>>24;
	buffer[2]=(val>>16)&0xFF;
	buffer[1]=(val>>8)&0xFF;
	buffer[0]=(val)&0xFF;

    }

    return aux_return_value;
}

static void free_IT_compressed_block (_IT_DecompressorState *p_state) {


	if (p_state->source_buffer)
		p_state->memory->free(p_state->source_buffer,IT_MEMORY_ALLOC_AUXILIARY);

	p_state->source_buffer=NULL;

}

static it_bool read_IT_compressed_block (_IT_DecompressorState *p_state,it_bool p_16bits) {

	it_u16 size;

	size=p_state->file->get_u16();

	if (p_state->file->eof_reached()) return it_true;

	p_state->source_buffer = (it_u32*)p_state->memory->alloc(4* ((size >> 2) + 2),IT_MEMORY_ALLOC_AUXILIARY);

	if (!p_state->source_buffer)
		return it_true;
	p_state->file->get_u8_array((it_u8*)p_state->source_buffer,size);

	if (p_state->file->eof_reached()) {

		free_IT_compressed_block(p_state);
		return it_true;
	}

	p_state->source_position = p_state->source_buffer;
	p_state->source_remaining_bits = 32;

	return it_false;
}

static it_bool load_sample_8bits_IT_compressed(_IT_DecompressorState *p_state,int p_buffsize) {


	it_u16 block_length;		/* length of compressed data block in samples */
	it_u16 block_position;		/* position in block */
	it_u8 bit_width;			/* actual "bit width" */
	it_u16 aux_value;			/* value read from file to be processed */
	it_s8 d1, d2;		/* integrator buffers (d2 for it2.15) */
	it_u32 write_pos=0;
	it_s8 v;			/* sample value */
	it_s16 v2;
	it_bool it215; /* is this an it215 module? */

	it215=p_state->it215;

	/* now unpack data till the dest buffer is full */

	while (p_buffsize) {
	/* read a new block of compressed data and reset variables */
		if ( read_IT_compressed_block(p_state,it_false) ) {
			_IT_ERROR_PRINT("Out of memory decompressing IT Sample");
			return it_true;
		}


		block_length = (p_buffsize < 0x8000) ? p_buffsize : 0x8000;

		block_position = 0;

		bit_width = 9;		/* start with width of 9 bits */

		d1 = d2 = 0;		/* reset integrator buffers */

	/* now uncompress the data block */
		while ( block_position < block_length ) {

			aux_value = _read_n_bits_from_IT_compressed_block(p_state,bit_width);			/* read bits */

			if ( bit_width < 7 ) { /* method 1 (1-6 bits) */

				if ( aux_value == (1 << (bit_width - 1)) ) { /* check for "100..." */

					aux_value = _read_n_bits_from_IT_compressed_block(p_state,3) + 1; /* yes -> read new width; */
					bit_width = (aux_value < bit_width) ? aux_value : aux_value + 1;
							/* and expand it */
					continue; /* ... next value */
				}

			} else if ( bit_width < 9 ) { /* method 2 (7-8 bits) */

				it_u8 border = (0xFF >> (9 - bit_width)) - 4;
							/* lower border for width chg */

				if ( aux_value > border && aux_value <= (border + 8) ) {

					aux_value -= border; /* convert width to 1-8 */
					bit_width = (aux_value < bit_width) ? aux_value : aux_value + 1;
							/* and expand it */
					continue; /* ... next value */
				}


			} else if ( bit_width == 9 ) { /* method 3 (9 bits) */

				if ( aux_value & 0x100 ) {			/* bit 8 set? */

					bit_width = (aux_value + 1) & 0xff;		/* new width... */
					continue;				/* ... and next value */
				}

			} else { /* illegal width, abort */


				free_IT_compressed_block(p_state);
				_IT_ERROR_PRINT("Sample has illegal BitWidth");
				return it_true;
			}

			/* now expand value to signed byte */
			if ( bit_width < 8 ) {

				it_u8 tmp_shift = 8 - bit_width;

				v=(aux_value << tmp_shift);
				v>>=tmp_shift;

			} else v = (it_s8) aux_value;

			/* integrate upon the sample values */
			d1 += v;
			d2 += d1;

			v2 = it215 ? d2 : d1;
			v2<<=8;

			/* ... and store it into the buffer */
			p_state->mixer->sample_set_frame(p_state->mixer,p_state->sample_id,write_pos++,0,v2);
			block_position++;

		}

		/* now subtract block lenght from total length and go on */
		free_IT_compressed_block(p_state);
		p_buffsize -= block_length;
	}


	return it_false;
}

static it_bool load_sample_16bits_IT_compressed(_IT_DecompressorState *p_state,int p_buffsize) {


	it_u16 block_length;		/* length of compressed data block in samples */
	it_u16 block_position;		/* position in block */
	it_u8 bit_width;			/* actual "bit width" */
	it_u32 aux_value;			/* value read from file to be processed */
	it_s16 d1, d2;		/* integrator buffers (d2 for it2.15) */
	it_s32 write_pos=0;		/* position in output buffer */
	it_s16 v;			/* sample value */
	it_bool it215; /* is this an it215 module? */

	it215=p_state->it215;


	while (p_buffsize) {
	/* read a new block of compressed data and reset variables */
		if ( read_IT_compressed_block(p_state,it_true) ) {

			return it_true;
		}


		block_length = (p_buffsize < 0x4000) ? p_buffsize : 0x4000;

		block_position = 0;

		bit_width = 17;		/* start with width of 9 bits */

		d1 = d2 = 0;		/* reset integrator buffers */

		while ( block_position < block_length ) {


			aux_value = _read_n_bits_from_IT_compressed_block(p_state,bit_width);			/* read bits */
			if ( bit_width < 7 ) { /* method 1 (1-6 bits) */

				if ( (signed)aux_value == (1 << (bit_width - 1)) ) { /* check for "100..." */

					aux_value = _read_n_bits_from_IT_compressed_block(p_state,4) + 1; /* yes -> read new width; */
					bit_width = (aux_value < bit_width) ? aux_value : aux_value + 1;
							/* and expand it */
					continue; /* ... next value */
				}

			} else if ( bit_width < 17 ) {

				it_u16 border = (0xFFFF >> (17 - bit_width)) - 8;

				if ( (int)aux_value > (int)border && (int)aux_value <= ((int)border + 16) ) {

					aux_value -= border; /* convert width to 1-8 */
					bit_width = (aux_value < bit_width) ? aux_value : aux_value + 1;
							/* and expand it */
					continue; /* ... next value */
				}


			} else if ( bit_width == 17 ) {

				if ( aux_value & 0x10000 ) {			/* bit 8 set? */

					bit_width = (aux_value + 1) & 0xff;		/* new width... */
					continue;				/* ... and next value */
				}

			} else { /* illegal width, abort */

				_IT_ERROR_PRINT("Sample has illegal BitWidth");

				free_IT_compressed_block(p_state);

				return it_true;
			}

			/* now expand value to signed byte */
			if ( bit_width < 16 ) {

				it_u8 tmp_shift = 16 - bit_width;

				v=(aux_value << tmp_shift);
				v>>=tmp_shift;

			} else v = (it_s16) aux_value;

			/* integrate upon the sample values */
			d1 += v;
			d2 += d1;

			/* ... and store it into the buffer */
			p_state->mixer->sample_set_frame(p_state->mixer,p_state->sample_id,write_pos++,0,it215 ? d2 : d1);
			block_position++;

		}

		/* now subtract block lenght from total length and go on */
		free_IT_compressed_block(p_state);
		p_buffsize -= block_length;
	}


	return it_false;

}


/* IT LOADER */

static IT_LoaderError it_load_it(IT_Song *p_song,IT_Instance *p_instance,IT_FileIO *p_file_io,IT_MemoryManager *p_memory,const char *p_filename,it_u8 p_loader_flags) {

	IT_FileIO *file;
	IT_FileIOError file_err;
	char it_magic[4];
	int order_count;
	int instrument_count;
	int sample_count;
	int pattern_count;
	int cwt;
	int cmwt;
	int flags;
	int i,j,k;
	it_u16 special;
	int message_length;
	int message_offset;
	char chibi[4];
	it_bool is_chibi=it_false;

	file=p_file_io;

	file_err = file->open(p_filename,it_false);
	if (file_err==IT_FILE_ERROR_CANT_OPEN) {
		return IT_LOADER_ERROR_FILE_CANT_OPEN;
	}
	if (file_err==IT_FILE_ERROR_IN_USE) {
		return IT_LOADER_ERROR_FILEIO_IN_USE;
	}

	/* LOAD HEADER */

	file->get_u8_array((it_u8*)it_magic,4);

	if (	it_magic[0]!='I' ||
		it_magic[1]!='M' ||
		it_magic[2]!='P' ||
		it_magic[3]!='M') {
		file->close();
		return IT_LOADER_ERROR_FILE_UNRECOGNIZED;
	}

	file->get_u8_array((it_u8*)p_song->name,26);
	p_song->name[25]=0;

	p_song->row_hl_min=file->get_u8();
	p_song->row_hl_maj=file->get_u8();

	if (p_song->row_hl_min==0)
		p_song->row_hl_min=4;
	if (p_song->row_hl_maj==0)
		p_song->row_hl_maj=4;

	order_count=file->get_u16();
	instrument_count=file->get_u16();
	sample_count=file->get_u16();
	pattern_count=file->get_u16();

	cwt=file->get_u16();
	cmwt=file->get_u16();
	flags=file->get_u16();

	p_song->flags=0;

	if (flags&1)
		p_song->flags|=IT_SONG_FLAG_STEREO;
	if (flags&4)
		p_song->flags|=IT_SONG_FLAG_USE_INSTRUMENTS;
	if (flags&8)
		p_song->flags|=IT_SONG_FLAG_LINEAR_PERIODS;
	if (flags&16)
		p_song->flags|=IT_SONG_FLAG_OLD_EFFECTS;
	if (flags&32)
		p_song->flags|=IT_SONG_FLAG_COMPATIBLE_GXX;

	special=file->get_u16();

	p_song->global_volume=file->get_u8();
	p_song->mixing_volume=file->get_u8();
	p_song->speed=file->get_u8();
	p_song->tempo=file->get_u8();
	file->get_u8(); /* Pitch Separation */
	file->get_u8(); /* Midi stuff */
	message_length=file->get_u16();
	message_offset=file->get_u32();	

	file->get_u8_array((it_u8*)chibi,4);
	is_chibi=(chibi[0]=='C' && chibi[1]=='H' && chibi[2]=='B' && chibi[3]=='I');

#ifdef LOADER_DEBUG

	printf("file: %s\n",p_filename);
	printf("name: %s\n",p_song->name);
	printf("version: %x\n",cwt);
	printf("compatible: %x\n",cmwt);
	printf("tempo: %i speed: %i vol: %i hl_min: %i hl_maj: %i\n",p_song->tempo,p_song->speed,p_song->global_volume,p_song->row_hl_min,p_song->row_hl_maj);
	printf("flags: %s:%s:%s:%s:%s:%s\n",
		((p_song->flags&IT_SONG_FLAG_STEREO)?"stereo":"mono"),
		((p_song->flags&IT_SONG_FLAG_USE_INSTRUMENTS)?"instrs":"samples"),
		((p_song->flags&IT_SONG_FLAG_LINEAR_PERIODS)?"linear":"amiga"),
		((p_song->flags&IT_SONG_FLAG_OLD_EFFECTS)?"oldfx":"itfx"),
		((p_song->flags&IT_SONG_FLAG_COMPATIBLE_GXX)?"oldgxx":"itgxx"),
		(is_chibi?"Chibi":"IT"));
	printf("orders: %i patterns: %i samples: %i instruments: %i\n",order_count,pattern_count,sample_count,instrument_count);

#endif


	for (i=0;i<IT_CONSTANT_MAX_CHANNELS;i++) {

		it_u8 panbyte=file->get_u8();
		it_u8 pan_dst=(panbyte<65) ? panbyte : 32;

		p_song->channels[i].flags=0;

		if (panbyte==100)
			p_song->channels[i].flags|=IT_CHANNEL_FLAG_SURROUND;
		if (panbyte>=128)
			p_song->channels[i].flags|=IT_CHANNEL_FLAG_MUTE;

		p_song->channels[i].pan=(it_s8)pan_dst-32;
	}

	for (i=0;i<IT_CONSTANT_MAX_CHANNELS;i++) {

		p_song->channels[i].volume=file->get_u8();
	}

#ifdef LOADER_DEBUG
	for (i=0;i<IT_CONSTANT_MAX_CHANNELS;i++) {

		printf("chan %i: pan %i vol: %i flags: %s:%s\n",
		       i,p_song->channels[i].pan,p_song->channels[i].volume,
		       ((p_song->channels[i].flags&IT_CHANNEL_FLAG_MUTE)?"mute":"play"),
		       ((p_song->channels[i].flags&IT_CHANNEL_FLAG_SURROUND)?"surround":"pan") );
	}
#endif

	if ( file->eof_reached() ) {

		file->close();
		return IT_LOADER_ERROR_FILE_CORRUPT;
	}

	/* LOAD ORDERS */

	file->seek(0xC0);


	for (i=0;i<order_count;i++) {

		it_u8 itorder=file->get_u8();
		it_u16 order=IT_CONSTANT_ORDER_EMPTY;

		if (i>=IT_CONSTANT_MAX_ORDERS)
			continue;

		if (itorder==254)  {

			order=IT_CONSTANT_ORDER_SKIP;

		} else if (itorder<200) {

			order=itorder;

		} else {

			order=IT_CONSTANT_ORDER_EMPTY;
		}

		p_song->order_list[i]=order;
	}

#ifdef LOADER_DEBUG

	printf("orders: ");
	for (i=0;i<order_count;i++) {
		if (i>0)
			printf(", ");
		if (p_song->order_list[i]<200)
			printf("%i",p_song->order_list[i]);
		else if (p_song->order_list[i]==IT_CONSTANT_ORDER_SKIP)
			printf("+++");
		else if (p_song->order_list[i]==IT_CONSTANT_ORDER_EMPTY)
			printf("...");
	}
	printf("\n");
#endif

	if ( file->eof_reached() ) {

		file->close();
		return IT_LOADER_ERROR_FILE_CORRUPT;
	}


	/* LOAD PATTERNS */

	p_song->channel_count=IT_CONSTANT_MAX_CHANNELS; /* should fix this so it's lower on smaller songs */

	for (i=0;i<pattern_count;i++) {

		int pattern_size=0;
		it_u16 pat_rows;
		it_u32 pattern_offset;
		IT_Pattern *pattern;

		if (i>=IT_CONSTANT_MAX_PATTERNS)
			break;

		/* Position where pattern offsets are stored */
		file->seek(0xC0+order_count+instrument_count*4+sample_count*4+i*4);
		pattern_offset=file->get_u32();
		if (pattern_offset==0) {
			continue; /* pattern not here */
		}

		file->seek(pattern_offset);

		pattern_size=file->get_u16();
		pat_rows=file->get_u16();
		file->get_u32(); /* reserved */

		pattern = p_memory->alloc( sizeof(IT_Pattern) + pattern_size -1 , IT_MEMORY_ALLOC_PATTERN );
		file->get_u8_array(pattern->data,pattern_size);
		pattern->rows=pat_rows;
		pattern->data_size=pattern_size;
		p_song->patterns[i]=pattern;

#ifdef LOADER_DEBUG

		printf("pattern %i: rows: %i datasize: %i\n",i,pattern->rows,pattern->data_size);
#endif
		if ( file->eof_reached() ) {

			file->close();
			return IT_LOADER_ERROR_FILE_CORRUPT;
		}

	}


	/* LOAD SAMPLES */

	for (i=0;i<sample_count;i++) {

		/* seek to sample pos */
		it_u8 flags;
		IT_Sample *sample;
		it_u32 location;
		it_u8 pan;
		it_bool compressed;
		it_bool has_data;

		IT_SampleData sample_data;

		file->seek(0xC0+order_count+instrument_count*4+i*4);		

		location=file->get_u32();
		file->seek( location );

		file->get_u8_array((it_u8*)it_magic,4);

		if (	it_magic[0]!='I' ||
			it_magic[1]!='M' ||
			it_magic[2]!='P' ||
			it_magic[3]!='S') {

			file->close();

			return IT_LOADER_ERROR_FILE_CORRUPT;
		}

		sample=(IT_Sample*)p_memory->alloc(sizeof(IT_Sample),IT_MEMORY_ALLOC_SAMPLE);
		p_song->samples[i]=sample;

		/* Ignore deprecated 8.3 filename */
		for (j=0;j<12;j++)
			file->get_u8();

		file->get_u8(); /* ignore zerobyte */

		sample->global_volume=file->get_u8();

		/* SAMPLE DATA FLAGS */
		flags=file->get_u8();

		sample_data.format=0;

		/*
			IT_SAMPLE_EXISTS=1,
			IT_SAMPLE_16BITS=2,
			IT_SAMPLE_STEREO=4,
			IT_SAMPLE_COMPRESSED=8,
			IT_SAMPLE_LOOPED=16,
			IT_SAMPLE_SUSTAIN_LOOPED=32,
			IT_SAMPLE_LOOP_IS_PINGPONG=64,
			IT_SAMPLE_SUSTAIN_LOOP_IS_PINGPONG=128
		*/

		has_data=(flags&1)?it_true:it_false;

		if (flags&16 || (flags&64))
			sample_data.loop_type=(flags&64)?IT_LOOP_PING_PONG:IT_LOOP_FORWARD;
		else
			sample_data.loop_type=IT_LOOP_DISABLED;

		if (flags&32 || (flags&128))
			sample_data.sustain_loop_type=(flags&127)?IT_LOOP_PING_PONG:IT_LOOP_FORWARD;
		else
			sample_data.sustain_loop_type=IT_LOOP_DISABLED;

		sample_data.format=(flags&2)?IT_SAMPLE_FORMAT_PCM16:IT_SAMPLE_FORMAT_PCM8;
		if (flags&4)
			sample_data.format|=IT_SAMPLE_FORMAT_PCM8;

		compressed=(flags&8)?it_true:it_false;

		sample->default_volume=file->get_u8();

		/* Name not needed */
		for (j=0;j<26;j++)
			file->get_u8();

		/* ??*/
		/*it_u8 convert_flag=*/file->get_u8();

		pan=file->get_u8();
		sample->pan_enabled=(pan&0x80)?it_true:it_false;
		sample->pan=pan&0x7F;

		sample_data.length=file->get_u32();

		sample_data.loop_begin= file->get_u32();
		sample_data.loop_end= file->get_u32();
		sample_data.c5_hz=file->get_u32();
		sample_data.sustain_loop_begin=file->get_u32();
		sample_data.sustain_loop_end=file->get_u32();

		location=file->get_u32();

		sample->vibrato_speed=file->get_u8();
		sample->vibrato_depth=file->get_u8();

		switch( file->get_u8() ) {
			/* Vibrato Wave: 0=sine, 1=rampdown, 2=square, 3=random */
			case 0: sample->vibrato_type=IT_VIBRATO_SINE; break;
			case 1: sample->vibrato_type=IT_VIBRATO_SAW; break;
			case 2: sample->vibrato_type=IT_VIBRATO_SQUARE; break;
			case 3: sample->vibrato_type=IT_VIBRATO_RANDOM; break;
			default: sample->vibrato_type=IT_VIBRATO_SINE; break;
		}

		sample->vibrato_rate=file->get_u8();

		sample->sample_id=IT_INVALID_SAMPLE_ID;

#ifdef LOADER_DEBUG

		printf("sample: %i\n",i);
		printf("\tglobalvol: %i defaultvol: %i pan: %i (%s)\n",sample->global_volume,sample->default_volume,sample->pan,(sample->pan_enabled?"on":"off"));
		printf("\tvibtype: %i vibspeed: %i vibdepth: %i vibrate: %i\n",sample->vibrato_type,sample->vibrato_speed,sample->vibrato_depth,sample->vibrato_rate);
#endif


		if (has_data) {


			IT_SampleID id;
			IT_Mixer *mixer;
			it_bool is_16=(sample_data.format&IT_SAMPLE_FORMAT_MASK)==IT_SAMPLE_FORMAT_PCM16;
			it_bool is_stereo=(sample_data.format&IT_SAMPLE_STEREO_FLAG)!=0;

#ifdef LOADER_DEBUG
			printf("sample_data: %i\n",i);
			printf("\t%s %s %s\n",(is_16?"16-bits":"8-bits"),(is_stereo?"stereo":"mono"),(compressed?"compressed":"raw"));
			printf("\tc5hz: %i length: %i\n",sample_data.c5_hz,sample_data.length);
			printf("\tloop: %s begin: %i end: %i\n",
			       (sample_data.loop_type==IT_LOOP_DISABLED)?"off":((sample_data.loop_type==IT_LOOP_FORWARD)?"forward":"ping-pong"),
				sample_data.loop_begin,
				sample_data.loop_end );
			printf("\tsustain loop: %s begin: %i end: %i\n",
			       (sample_data.sustain_loop_type==IT_LOOP_DISABLED)?"off":((sample_data.sustain_loop_type==IT_LOOP_FORWARD)?"forward":"ping-pong"),
				sample_data.sustain_loop_begin,
				sample_data.sustain_loop_end );

#endif

			file->seek(location);

			mixer = it_instance_get_mixer(p_instance);

			id = mixer->sample_create(mixer,&sample_data);

			if (id==IT_INVALID_SAMPLE_ID) {
				_IT_ERROR_PRINT("Invalid sample parameters");
				continue;
			}

			if (!compressed) {

				int chans=is_stereo?2:1;
				int c;
				int sidx;

				for (c=0;c<chans;c++) {

					for (sidx=0;sidx<sample_data.length;sidx++) {

						if (is_16) {
							it_u16 s=file->get_u16();
							it_s16 *v=(it_s16*)&s;
							mixer->sample_set_frame(mixer,id,i,c,*v);
						} else {

							it_u8 s=file->get_u8();
							it_s8 *v=(it_s8*)&s;
							it_s16 v16=(*v) << 8;
							mixer->sample_set_frame(mixer,id,i,c,v16);
						}

					}

				};
			} else {

				_IT_DecompressorState state;
				state.file=p_file_io;
				state.memory=p_memory;
				state.it215=cwt>=0x215;
				state.mixer=mixer;
				state.sample_id=id;

				if (is_16) {

					if ( load_sample_16bits_IT_compressed(&state,sample_data.length) ) {

						mixer->sample_free(mixer,id);
						_IT_ERROR_PRINT("Sample Compression Corrupted.");
						continue;
					}
				} else {

					if ( load_sample_8bits_IT_compressed(&state,sample_data.length) ) {

						mixer->sample_free(mixer,id);
						_IT_ERROR_PRINT("Sample Compression Corrupted.");
						continue;
					}

				}

				/* i have no idea how to handle stereo compressed, does it exist? */
			}

			sample->sample_id=id;
		}

		if ( file->eof_reached() ) {

			file->close();
			return IT_LOADER_ERROR_FILE_CORRUPT;
		}

	}

	if ( file->eof_reached() ) {

		file->close();
		return IT_LOADER_ERROR_FILE_CORRUPT;
	}

	/* LOAD INSTRUMENTS */

	for (i=0;i<instrument_count;i++) {

		it_u32 location;
		IT_Instrument *instrument;
		int fade;
		it_u8 pan;
		it_u8 samples;
		it_u8 cutoff;
		it_u8 resonance;

		file->seek(0xC0+order_count+i*4);
		location=file->get_u32();
		file->seek( location );

		file->get_u8_array((it_u8*)it_magic,4);


		if (	it_magic[0]!='I' ||
			       it_magic[1]!='M' ||
			       it_magic[2]!='P' ||
			       it_magic[3]!='I') {
			_IT_ERROR_PRINT("IT Loader Instrument: Failed Identifier");

			file->close();
			return IT_LOADER_ERROR_FILE_CORRUPT;
		}

		instrument=p_memory->alloc(sizeof(IT_Instrument),IT_MEMORY_ALLOC_INSTRUMENT);
		p_song->instruments[i]=instrument;

		/* Ignore deprecated 8.3 filename field */
		for (j=0;j<12;j++)
			file->get_u8();

		/* Ignore zerobyte */
		file->get_u8();		/* (byte) Instrument type (always 0) */

		switch( file->get_u8() ) { /* New Note Action [0,1,2,3] */
			case 0: instrument->nna_type=IT_NNA_NOTE_CUT ; break;
			case 1: instrument->nna_type=IT_NNA_NOTE_CONTINUE ; break;
			case 2: instrument->nna_type=IT_NNA_NOTE_OFF  ; break;
			case 3: instrument->nna_type=IT_NNA_NOTE_FADE  ; break;
		};
		switch( file->get_u8() ) { /*  Duplicate Check Type */
			case 0: instrument->dc_type=IT_DCT_DISABLED; break ;
			case 1: instrument->dc_type=IT_DCT_NOTE; break ;
			case 2: instrument->dc_type=IT_DCT_SAMPLE; break ;
			case 3: instrument->dc_type=IT_DCT_INSTRUMENT; break ;
		}
		switch( file->get_u8() ) { /* Duplicate Check Action */
			case 0: instrument->dc_action=IT_DCA_NOTE_CUT; break ;
			case 1: instrument->dc_action=IT_DCA_NOTE_OFF; break ;
			case 2: instrument->dc_action=IT_DCA_NOTE_FADE; break ;
		}

		fade = file->get_u16();

		/* needs to be clipped because of horrible modplug doings */
		if (fade>IT_CONSTANT_MAX_FADEOUT)
			fade=IT_CONSTANT_MAX_FADEOUT;

		instrument->fadeout=fade;
		instrument->pan_pitch_separation=file->get_u8();
		instrument->pan_pitch_center=file->get_u8();
		instrument->global_volume=file->get_u8();

		pan=file->get_u8();
		instrument->pan=(pan&0x7F)-32;
		instrument->pan_enabled=((pan&0x80)==0)?it_true:it_false;
		instrument->random_volume=file->get_u8();
		instrument->random_pan=file->get_u8();


		file->get_u16(); /* empty (version) */

		samples=file->get_u8();

		file->get_u8(); /* empty */

		for (j=0;j<26;j++)
			file->get_u8();

		cutoff=file->get_u8();
		instrument->filter_cutoff=cutoff&0x7F;
		instrument->filter_cutoff_enabled=(cutoff&0x80)?it_true:it_false;

		resonance=file->get_u8();
		instrument->filter_resonance=resonance&0x7F;
		instrument->filter_resonance_enabled=(resonance&0x80)?it_true:it_false;

		file->get_u32(); /* MIDI, IGNORED! */

		/* Note -> Sample table */

		for (j=0;j<IT_CONSTANT_MAX_NOTES;j++) {


			it_u8 note=file->get_u8();
			it_u8 samp=file->get_u8();
			if (note>=IT_CONSTANT_MAX_NOTES)
				note=0;

			instrument->note_note[j]=note;
			instrument->note_sample[j]=samp;

		}

#ifdef LOADER_DEBUG
		printf("instrument: %i\n",i);
		printf("\tglobalvol: %i fadeout: %i randomvol: %i\n",instrument->global_volume,instrument->fadeout,instrument->random_volume);
		printf("\tpan: %i (%s) randompan: %i\n",instrument->pan,(instrument->pan_enabled?"on":"off"),instrument->random_pan);
		printf("\tppan_sep: %i ppan_center: %i\n",instrument->pan_pitch_separation,instrument->pan_pitch_center);
		printf("\tcutoff: %i (%s) resonance: %i (%s)\n",instrument->filter_cutoff,(instrument->filter_cutoff_enabled?"on":"off"),instrument->filter_resonance,(instrument->filter_resonance_enabled?"on":"off"));
#endif

		for(j=0;j<3;j++) {
			/* load all 3 envelopes */
			IT_Envelope *envelope;
			it_u8 flags;

			if (j==0)
				envelope=&instrument->volume_envelope;
			if (j==1)
				envelope=&instrument->pan_envelope;
			if (j==2)
				envelope=&instrument->pitch_filter_envelope;


			flags=file->get_u8();

			envelope->flags=0;

			if(flags&1)
				envelope->flags|=IT_ENVELOPE_ENABLED;

			if(flags&2)
				envelope->flags|=IT_ENVELOPE_LOOP_ENABLED;

			if(flags&4)
				envelope->flags|=IT_ENVELOPE_SUSTAIN_ENABLED;

			if(flags&8)
				envelope->flags|=IT_ENVELOPE_CARRY_ENABLED;

			if(flags&128)
				envelope->flags|=IT_ENVELOPE_IS_FILTER;

			envelope->point_count=file->get_u8();
			envelope->loop_begin_index=file->get_u8();
			envelope->loop_end_index=file->get_u8();
			envelope->sustain_loop_begin_index=file->get_u8();
			envelope->sustain_loop_end_index=file->get_u8();


			for (k=0;k<25;k++) {

				it_u8 height=file->get_u8();
				it_s8 sheight = (it_s8)height;
				it_u16 offset=file->get_u16();

				if (i>=envelope->point_count)
					continue;

				envelope->points[k].offset=offset;
				envelope->points[k].value=sheight;

			}

			file->get_u8(); /* empty byte? */

			/* Some envelopes come without points (modplug??), fix it */

			if (envelope->point_count==0) {

				it_s8 val;

				if (j==0)
					val=64;
				else
					val=0;

				envelope->points[0].offset=0;
				envelope->points[0].value=val;
				envelope->points[1].offset=16;
				envelope->points[1].value=val;
				envelope->point_count=2;
			}

#ifdef LOADER_DEBUG


			printf("\tEnvelope: %s\n",(i==0?"Volume":(i==1?"Pan":"Pitch/Filter")));
			printf("\t\tflags:");
			if (envelope->flags&IT_ENVELOPE_ENABLED)
				printf(" enabled");
			if (envelope->flags&IT_ENVELOPE_LOOP_ENABLED)
				printf(" loop");
			if (envelope->flags&IT_ENVELOPE_SUSTAIN_ENABLED)
				printf(" sustain");
			if (envelope->flags&IT_ENVELOPE_CARRY_ENABLED)
				printf(" carry");
			if (envelope->flags&IT_ENVELOPE_IS_FILTER)
				printf(" filter");
			printf("\n");

			printf("\t\tloop begin: %i end %i\n",envelope->loop_begin_index,envelope->loop_end_index);
			printf("\t\tsustain loop begin: %i end %i\n",envelope->sustain_loop_begin_index,envelope->sustain_loop_end_index);
			printf("\t\tpoint count: %i\n",envelope->point_count);
			printf("\t\tpoints:");
			for(k=0;k<envelope->point_count;k++) {
				printf(" (%i:%i)",envelope->points[k].offset,envelope->points[k].value);
			}
			printf("\n");
#endif

		}

		if ( file->eof_reached() ) {

			file->close();
			return IT_LOADER_ERROR_FILE_CORRUPT;
		}

	}

	/* LOAD MESSAGE */

	if (special&1 && message_length<(1<<16)) { /* message here */

		file->seek(message_offset);
		p_song->message=(char*)p_memory->alloc(message_length+1,IT_MEMORY_ALLOC_MESSAGE);
		file->get_u8_array((it_u8*)p_song->message,message_length);
		p_song->message[message_length]=0;
		for(i=0;i<message_length;i++) {
			if (p_song->message[i]=='\r')
				p_song->message[i]='\n'; /* make it more C standarized */
		}

#ifdef LOADER_DEBUG

		printf("message:\n%s\n",p_song->message);
#endif
		if ( file->eof_reached() ) {

			file->close();
			return IT_LOADER_ERROR_FILE_CORRUPT;
		}
	}


	return IT_LOADER_OK;
}



IT_LoaderFunc it_default_loaders[]={
	it_load_it,
	0
};



